﻿namespace Example.Cryptography
{
    using System;
    using System.Globalization;
    using System.Security.Cryptography;
    using System.Xml;
    using System.Xml.XPath;

    using Xunit;

    public class RijndaelSettingsFacts
    {
        [Fact]
        public void Roundtrip()
        {
            using (var obj = new RijndaelSettings(new RijndaelManaged()))
            {
                const string expected = "test";
                var actual = obj.Decrypt(obj.Encrypt(new RijndaelPlaintext(expected))).ToString();

                Assert.Equal(expected, actual);
            }
        }

        [Fact]
        public void Roundtrip_Empty()
        {
            using (var obj = new RijndaelSettings(new RijndaelManaged()))
            {
                var expected = string.Empty;
                var actual = obj.Decrypt(obj.Encrypt(new RijndaelPlaintext(expected))).ToString();

                Assert.Equal(expected, actual);
            }
        }

        [Fact]
        public void ctor()
        {
            using (var obj = new RijndaelSettings())
            {
                Assert.NotNull(obj);
            }
        }

        [Fact]
        public void ctor_IXPathNavigable()
        {
            var algorithm = new RijndaelManaged();
            var key = Convert.ToBase64String(algorithm.Key);
            var iv = Convert.ToBase64String(algorithm.IV);

            var config = new XmlDocument();
            config.LoadXml(string.Format(CultureInfo.InvariantCulture, "<rijndael key=\"{0}\" iv=\"{1}\"/>", key, iv));

            using (var obj = new RijndaelSettings(config.DocumentElement))
            {
                Assert.NotNull(obj);
            }
        }

        [Fact]
        public void ctor_IXPathNavigableBad()
        {
            var config = new XmlDocument();
            config.LoadXml("<rijndael key=\"abc\" iv=\"123\"/>");

            Assert.Throws<FormatException>(() => new RijndaelSettings(config.DocumentElement));
        }

        [Fact]
        public void ctor_IXPathNavigableNull()
        {
            Assert.Throws<ArgumentNullException>(() => new RijndaelSettings(null as IXPathNavigable));
        }

        [Fact]
        public void ctor_RijndaelManaged()
        {
            using (var obj = new RijndaelSettings(new RijndaelManaged()))
            {
                Assert.NotNull(obj);
            }
        }

        [Fact]
        public void ctor_RijndaelManagedNull()
        {
            Assert.Throws<ArgumentNullException>(() => new RijndaelSettings(null as RijndaelManaged));
        }

        [Fact]
        public void ctor_byteEmpty_byte()
        {
            Assert.Throws<CryptographicException>(() => new RijndaelSettings(
                                                            new byte[]
                                                                {
                                                                }, 
                                                            new RijndaelManaged().IV));
        }

        [Fact]
        public void ctor_byteNull_byte()
        {
            Assert.Throws<ArgumentNullException>(() => new RijndaelSettings(null, new RijndaelManaged().IV));
        }

        [Fact]
        public void ctor_byte_byte()
        {
            var algorithm = new RijndaelManaged();

            using (var obj = new RijndaelSettings(algorithm.Key, algorithm.IV))
            {
                Assert.NotNull(obj);
            }
        }

        [Fact]
        public void ctor_byte_byteEmpty()
        {
            Assert.Throws<CryptographicException>(() => new RijndaelSettings(new RijndaelManaged().Key, new byte[] { }));
        }

        [Fact]
        public void ctor_byte_byteNull()
        {
            Assert.Throws<ArgumentNullException>(() => new RijndaelSettings(new RijndaelManaged().Key, null));
        }

        [Fact]
        public void ctor_string()
        {
            var algorithm = new RijndaelManaged();
            var key = Convert.ToBase64String(algorithm.Key);
            var iv = Convert.ToBase64String(algorithm.IV);

            var config = string.Format(CultureInfo.InvariantCulture, "<rijndael key=\"{0}\" iv=\"{1}\"/>", key, iv);

            using (var obj = new RijndaelSettings(config))
            {
                Assert.NotNull(obj);
            }
        }

        [Fact]
        public void ctor_stringBad()
        {
            Assert.Throws<FormatException>(() => new RijndaelSettings("<rijndael key=\"abc\" iv=\"123\"/>"));
        }

        [Fact]
        public void ctor_stringEmpty()
        {
            Assert.Throws<XmlException>(() => new RijndaelSettings(string.Empty));
        }

        [Fact]
        public void ctor_stringEmpty_string()
        {
            Assert.Throws<CryptographicException>(() => new RijndaelSettings(Convert.ToBase64String(new RijndaelManaged().Key), string.Empty));
        }

        [Fact]
        public void ctor_stringNull()
        {
            Assert.Throws<ArgumentNullException>(() => new RijndaelSettings(null as string));
        }

        [Fact]
        public void ctor_stringNull_string()
        {
            Assert.Throws<ArgumentNullException>(() => new RijndaelSettings(Convert.ToBase64String(new RijndaelManaged().Key), null));
        }

        [Fact]
        public void ctor_string_string()
        {
            var algorithm = new RijndaelManaged();
            var key = Convert.ToBase64String(algorithm.Key);
            var iv = Convert.ToBase64String(algorithm.IV);

            using (var obj = new RijndaelSettings(key, iv))
            {
                Assert.NotNull(obj);
            }
        }

        [Fact]
        public void ctor_string_stringEmpty()
        {
            Assert.Throws<CryptographicException>(() => new RijndaelSettings(string.Empty, Convert.ToBase64String(new RijndaelManaged().IV)));
        }

        [Fact]
        public void ctor_string_stringNull()
        {
            Assert.Throws<ArgumentNullException>(() => new RijndaelSettings(null, Convert.ToBase64String(new RijndaelManaged().IV)));
        }

        [Fact]
        public void is_IDisposable()
        {
            using (var obj = new RijndaelSettings())
            {
                Assert.IsAssignableFrom<IDisposable>(obj);
            }
        }

        [Fact]
        public void op_Decrypt_RijndaelCiphertext()
        {
            const string key = "A9sIqCB/26j0X+wvSHJCF7myuwSocllYV0PFZiRwsWw=";
            const string iv = "BjVMdXyFXZCmaIcD5UHCAw==";

            using (var obj = new RijndaelSettings(key, iv))
            {
                const string expected = "test";
                var actual = obj.Decrypt(new RijndaelCiphertext("IPr11UCGd6RkyHvTWdiCFQ==")).ToString();

                Assert.Equal(expected, actual);
            }
        }

        [Fact]
        public void op_Decrypt_RijndaelCiphertextNull()
        {
            Assert.Throws<ArgumentNullException>(() => new RijndaelSettings().Decrypt(null));
        }

        [Fact]
        public void op_Encrypt_RijndaelPlaintext()
        {
            using (var obj = new RijndaelSettings())
            {
                Assert.NotNull(obj.Encrypt(new RijndaelPlaintext("test")));
            }
        }

        [Fact]
        public void op_Encrypt_RijndaelPlaintextNull()
        {
            Assert.Throws<ArgumentNullException>(() => new RijndaelSettings().Encrypt(null));
        }

        [Fact]
        public void op_ToString()
        {
            const string key = "A9sIqCB/26j0X+wvSHJCF7myuwSocllYV0PFZiRwsWw=";
            const string iv = "BjVMdXyFXZCmaIcD5UHCAw==";

            using (var obj = new RijndaelSettings(key, iv))
            {
                const string expected = "<rijndael key=\"A9sIqCB/26j0X+wvSHJCF7myuwSocllYV0PFZiRwsWw=\" iv=\"BjVMdXyFXZCmaIcD5UHCAw==\"/>";
                var actual = obj.ToString();

                Assert.Equal(expected, actual);
            }
        }
    }
}